/*
 * OREKIT-X
 * Copyright 2002-2008 CS Communication & Systemes
 * 
 * Parts of this software package have been licensed to CS
 * Communication & Systemes (CS) under one or more contributor license
 * agreements.  See the NOTICE file distributed with this work for
 * additional information.
 *  
 * This is an experimental copy of OREKIT from www.orekit.org.
 * Please use the original OREKIT from orekit.org for normal work
 * unrelated to this research project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You
 * may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package ore.tle;

import ore.errors.OrekitException;
import ore.time.AbsoluteDate;
import ore.time.TimeScalesFactory;

/** 
 * This class contains methods to compute propagated coordinates with
 * the SDP4 model.
 * 
 * This implementation is largely inspired from the paper and source
 * code <a
 * href="http://www.celestrak.com/publications/AIAA/2006-6753/">Revisiting
 * Spacetrack Report #3</a> and is fully compliant with its results
 * and tests cases.
 * 
 * @see TLEPropagator
 * 
 * @author Felix R. Hoots, Ronald L. Roehrich, December 1980 (original fortran)
 * @author David A. Vallado, Paul Crawford, Richard Hujsak, T.S. Kelso (C++ translation and improvements)
 * @author Fabien Maussion (java translation)
 */
abstract class SDP4
    extends TLEPropagator
{
    /** 
     * New perigee argument.
     */
    protected double omgadf;
    /** 
     * New mean motion.
     */
    protected double xn;
    /** 
     * Parameter for xl computation.
     */
    protected double xll;
    /** 
     * New eccentricity.
     */
    protected double em;
    /** 
     * New inclination.
     */
    protected double xinc;


    /** 
     * @param initialTLE The TLE to propagate.
     * @exception OrekitException if some specific error occurs
     */
    protected SDP4(TLE initialTLE) throws OrekitException {
        super(initialTLE);
    }


    /** 
     * Initialization proper to each propagator (SGP or SDP).
     * @exception OrekitException UTC time steps can't be read
     */
    protected void sxpInitialize() throws OrekitException {
        this.luniSolarTermsComputation();
    }
    /** 
     * Propagation proper to each propagator (SGP or SDP).
     * @param tSince the offset from initial epoch (minutes)
     */
    protected void sxpPropagate(final double tSince) {
        /*
         * Update for secular gravity and atmospheric drag
         */
        this.omgadf = (tle.getPerigeeArgument() + (this.omgdot * tSince));
        final double xnoddf = (tle.getRaan() + (this.xnodot * tSince));
        final double tSinceSq = (tSince * tSince);
        this.xnode = (xnoddf + (this.xnodcf * tSinceSq));
        this.xn = this.xn0dp;
        /*
         * Update for deep-space secular effects
         */
        this.xll = (tle.getMeanAnomaly() + (this.xmdot * tSince));

        this.deepSecularEffects(tSince);

        final double tempa = (1 - (this.c1 * tSince));
        this.a   = (Math.pow((TLEConstants.XKE / this.xn), TLEConstants.TWO_THIRD) * tempa * tempa);
        this.em -= (tle.getBStar() * this.c4 * tSince);
        /*
         * Update for deep-space periodic effects
         */
        this.xll += (this.xn0dp * this.t2cof * tSinceSq);

        this.deepPeriodicEffects(tSince);

        this.xl = (this.xll + this.omgadf + this.xnode);
        /*
         * Dundee change:  Reset cosio,  sinio for new xinc:
         */
        this.cosi0 = Math.cos(this.xinc);
        this.sini0 = Math.sin(this.xinc);
        this.e = this.em;
        this.i = this.xinc;
        this.omega = this.omgadf;
        /*
         * End of calculus, go for PV computation
         */
    }

    /** 
     * Compute SPACETRACK#3 compliant earth rotation angle.
     * 
     * @param date Current date
     * 
     * @return The ERA (rad)
     * 
     * @exception OrekitException when UTC time steps can't be read
     */
    protected static double thetaG(final AbsoluteDate date) throws OrekitException {
        /*
         * Reference:  The 1992 Astronomical Almanac, page B6.
         */
        final double omega_E = 1.00273790934;
        final double jd = ((date.durationFrom(AbsoluteDate.JULIAN_EPOCH) +
                            date.timeScalesOffset(TimeScalesFactory.getUTC(), TimeScalesFactory.getTT()))
                           / 86400);
        /*
         * Earth rotations per sidereal day (non-constant)
         */
        final double UT = ((jd + 0.5) % 1);
        final double seconds_per_day = 86400.0;
        final double jd_2000 = 2451545.0;   /* 1.5 Jan 2000 = JD 2451545.
                                             */
        final double t_cen = ((jd - UT - jd_2000) / 36525.0);
        double GMST = (24110.54841 +
                       (t_cen * (8640184.812866 + t_cen * (0.093104 - t_cen * 6.2E-6))));
        GMST = (GMST + seconds_per_day * omega_E * UT) % seconds_per_day;
        if (GMST < 0.0) {
            GMST += seconds_per_day;
        }

        return ((2 * Math.PI * GMST) / seconds_per_day);
    }
    /** 
     * Compute Luni - Solar terms from initial coordinates and epoch.
     * @exception OrekitException when UTC time steps can't be read
     */
    protected abstract void luniSolarTermsComputation() throws OrekitException;
    /** 
     * Compute secular terms from current coordinates and epoch.
     * @param t offset from initial epoch (min)
     */
    protected abstract void deepSecularEffects(double t);
    /** 
     * Compute periodic terms from current coordinates and epoch.
     * @param t offset from initial epoch (min)
     */
    protected abstract void deepPeriodicEffects(double t);

}
